﻿namespace SharpF2.Services
{
	using System.Collections.Generic;
	using System.IO;
	using ModelView;

	/// <summary>
	/// Watch many directory event
	/// </summary>
	public class DirectoryScannerManager
	{
		#region Events

		/// <summary>
		/// Event raised on file created or deleted in the watched directory.
		/// </summary>
		public event FileSystemEventHandler DirectoryChangeEvent;

		/// <summary>
		/// Event raised on file renamed in the watched directory.
		/// </summary>
		public event RenamedEventHandler ObservedFileRenamedEvent;

		#endregion

		#region Constructors

		protected DirectoryScannerManager()
		{
		}

		#endregion

		#region Private Attributes

		private static readonly DirectoryScannerManager instance = new DirectoryScannerManager();

		/// <summary>
		/// Container of DirectoryScanner.
		/// </summary>
		private readonly Dictionary<string, DirectoryScanner> directoryScanners =
			new Dictionary<string, DirectoryScanner>();

		#endregion

		#region Public Properties

		public static DirectoryScannerManager Instance
		{
			get { return instance; }
		}

		#endregion

		#region Public Members

		/// <summary>
		/// Add a FileItem to observation, prevent client when there are name Conflicts.
		/// </summary>
		/// <param name="fileItem">The FileItem to add.</param>
		/// <returns>
		/// True if the file was added, false when the files was already added.
		/// </returns>
		public bool AddFile(FileItem fileItem)
		{
			var fileAdded = true;
			if (!directoryScanners.ContainsKey(fileItem.InputDirectoryPath))
			{
				var directoryScanner = new DirectoryScanner();
				if (directoryScanner.Start(fileItem.InputFilePath))
				{
					directoryScanners.Add(fileItem.InputDirectoryPath, directoryScanner);
					directoryScanner.DirectoryChangeEvent += DirectoryScanner_DirectoryChangeEvent;
					directoryScanner.ObservedFileRenamedEvent += DirectoryScanner_ObservedFileRenamedEvent;
				}
			}
			else if (!directoryScanners[fileItem.InputDirectoryPath].ObserveFile(fileItem.InputFilePath))
			{
				directoryScanners[fileItem.InputDirectoryPath].AddFileToObservation(fileItem.InputFilePath);
			}
			else
			{
				fileAdded = false;
			}

			return fileAdded;
		}

		/// <summary>
		/// Remove a file from observation.
		/// </summary>
		/// <param name="fileItem">The FileItem to remove.</param>
		public void RemoveFileToObservation(FileItem fileItem)
		{
			DirectoryScanner directoryScanner;
			if (!directoryScanners.TryGetValue(fileItem.InputDirectoryPath, out directoryScanner)) return;
			if (!directoryScanner.RemoveFileToObservation(fileItem.InputFilePath)) return;
			directoryScanner.DirectoryChangeEvent -= DirectoryScanner_DirectoryChangeEvent;
			directoryScanner.ObservedFileRenamedEvent -= DirectoryScanner_ObservedFileRenamedEvent;
			directoryScanners.Remove(fileItem.InputDirectoryPath);
		}

		/// <summary>
		/// Update the conflicts of a collection of FileItem.
		/// </summary>
		/// <param name="fileItems">The collection of FileItem.</param>
		public void UpdateConflicts(ref IEnumerable<FileItem> fileItems)
		{
			foreach (var directoryScanner in directoryScanners.Values)
				directoryScanner.UpdateConflicts(ref fileItems);
		}

		#endregion

		#region Private Members

		/// <summary>
		/// Raise an event signaling clients that an observed file has been created or 
		/// deleted in the watched folder.
		/// </summary>
		/// <param name="sender">The sender of the event.</param>
		/// <param name="e">Parameters of the event.</param>
		private void DirectoryScanner_DirectoryChangeEvent(object sender, FileSystemEventArgs e)
		{
			var handler = DirectoryChangeEvent;
			if (handler != null)
			{
				handler(sender, e);
			}
		}

		/// <summary>
		/// Raise an event signaling clients that an observed file has been renamed 
		/// in the watched folder.
		/// </summary>
		/// <param name="sender">The sender of the event.</param>
		/// <param name="e">Parameters of the event.</param>
		private void DirectoryScanner_ObservedFileRenamedEvent(object sender, RenamedEventArgs e)
		{
			var handler = ObservedFileRenamedEvent;
			if (handler != null)
			{
				handler(sender, e);
			}
		}

		#endregion
	}
}